package async

import (
	"syscall"
	"runtime"
)

type Handle struct {
	hash syscall.AidT
	result callData
}

func createHandle() *Handle {
	result := new(Handle)
	runtime.SetFinalizer(result, func(m *Handle) {
		if m.hash != 0 {
			async_forget(m.hash)
		}
	})
	return result
}

// Same as WaitTimeout(), except that it waits forever.
func (msg *Handle) Wait() (rc syscall.Errno, ret1, ret2, ret3, ret4, ret5 syscall.Arg) {
	runtime.SetFinalizer(msg, nil)
	var ret syscall.Arg
	async_wait_for(msg.hash, &ret)

	return syscall.Errno(ret), msg.result.args[1],
		msg.result.args[2], msg.result.args[3], msg.result.args[4], msg.result.args[5]
}

// If the returned error is syscall.ETIMEDOUT, Wait() or Forget() still have to be called.
// If any other error is returned, the call failed and no further cleanup is needed.
func (msg *Handle) WaitTimeout(useconds int) (rc syscall.Errno, ret1, ret2, ret3, ret4, ret5 syscall.Arg) {
	runtime.SetFinalizer(msg, nil)
	var ret syscall.Arg
	if rc := async_wait_timeout(msg.hash, &ret, syscall.SusecondsT(useconds)); rc != 0 {
		return syscall.Errno(rc), 0, 0, 0, 0, 0
	}

	return syscall.Errno(ret), msg.result.args[1],
		msg.result.args[2], msg.result.args[3], msg.result.args[4], msg.result.args[5]
}

// Release the handle. Every async request must either be waited for or forgotten.
func (msg *Handle) Forget() {
	runtime.SetFinalizer(msg, nil)
	async_forget(msg.hash)
}

////////////////////////////////////////////////////////////////////////////////////////////////////

func (exch *Exchange) Request(method, arg1, arg2, arg3, arg4, arg5 syscall.Arg) (rc syscall.Errno, ret1, ret2, ret3, ret4, ret5 syscall.Arg) {
	if arg5 == 0 {
		rc = async_req_fast(exch.ptr, method, arg1, arg2, arg3, arg4, &ret1, &ret2, &ret3, &ret4, &ret5)
	} else {
		rc = async_req_slow(exch.ptr, method, arg1, arg2, arg3, arg4, arg5, &ret1, &ret2, &ret3, &ret4, &ret5)
	}
	return
}

func (exch *Exchange) RequestAsync(method, arg1, arg2, arg3, arg4, arg5 syscall.Arg) *Handle {
	msg := createHandle()
	if arg5 == 0 {
		msg.hash = async_send_fast(exch.ptr, method, arg1, arg2, arg3, arg4, &msg.result)
	} else {
		msg.hash = async_send_slow(exch.ptr, method, arg1, arg2, arg3, arg4, arg5, &msg.result)
	}
	if msg.hash == 0 {
		return nil
	}
	return msg
}
